from django import forms
from django.core.handlers.wsgi import WSGIRequest
import random

from web import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.conf import settings
from utils.aliyun.sms import send_sms_single
from django_redis import get_redis_connection
from utils.encrypt import md5
from web.forms.bootstrap import BootStrapForm


class RegisterModelForm(BootStrapForm, forms.ModelForm):
    password = forms.CharField(
        label='密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '密码长度不能小于8个字符',
            'max_length': '密码长度不能大于64个字符'
        },
        widget=forms.PasswordInput(),
    )

    # 新增
    confirm_password = forms.CharField(
        label='重复密码',
        min_length=8,
        max_length=64,
        error_messages={
            'min_length': '重复密码长度不能小于8个字符',
            'max_length': '重复密码长度不能大于64个字符'
        },
        widget=forms.PasswordInput(),
    )

    # 原有则覆盖
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    code = forms.CharField(label='验证码')

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']

    def clean_username(self):
        username = self.cleaned_data['username']
        if models.UserInfo.objects.filter(username=username).exists():
            raise ValidationError("用户名已存在")
        return username

    def clean_email(self):
        email = self.cleaned_data['email']
        if models.UserInfo.objects.filter(email=email).exists():
            raise ValidationError("邮箱已被使用")
        return email

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return md5(pwd)

    def clean_confirm_password(self):
        # 优先级, cleaned_data中只能取校验过的数据(顺序)
        pwd = self.cleaned_data.get('password')

        confirm_pwd = md5(self.cleaned_data['confirm_password'])
        if pwd != confirm_pwd:
            raise ValidationError("两次密码不一致")
        return confirm_pwd

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        if models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists():
            raise ValidationError("手机号已被注册")
        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data.get('mobile_phone')
        if not mobile_phone:
            return code
        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        # 去除空格
        if code.strip() != redis_code.decode('utf-8'):
            raise ValidationError("验证码错误, 请重新输入")
        return code


class SendSmsForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    def __init__(self, request: WSGIRequest, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        """手机号校验"""
        mobile_phone = self.cleaned_data['mobile_phone']

        # 判断短信模板是否有问题
        tpl = self.request.GET.get('tpl')
        template = settings.ALI_SMS_TEMPLATE[tpl]
        if not template:
            # raise ValidationError与add_error()等价, 并且还可以终止程序的执行
            # self.add_error('mobile_phone', '短信模板错误')
            raise ValidationError("短信模板错误")

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == 'login':
            if not exists:
                raise ValidationError("手机号不存在")
        else:
            # 校验数据库中是否已有手机号
            if exists:
                raise ValidationError("手机号已存在")

        code = random.randint(10000, 99999)

        # 发送短信
        sms = send_sms_single(mobile_phone, template, code)
        if not sms:
            raise ValidationError(f'短信发送失败, {sms}')
        # 验证码写入redis
        conn = get_redis_connection()
        # mobile字段值为code, 60s超时
        conn.set(mobile_phone, code, ex=60)
        return mobile_phone


class LoginSmsModelForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(
        label='手机号',
        validators=[RegexValidator(r'^(1[3|4|5|6|7|8|9])\d{9}$', '手机号格式错误'), ]
    )

    sms_code = forms.CharField(
        label='验证码',
        widget=forms.TextInput()
    )

    def clean_mobile_phone(self):
        # 校验手机号是否存在
        mobile_phone = self.cleaned_data['mobile_phone']
        user_obj = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first()
        if not user_obj:
            raise ValidationError("手机号不存在")
        return user_obj

    def clean_sms_code(self):
        # 校验验证码是否正确
        sms_code = self.cleaned_data['sms_code']
        user_obj = self.cleaned_data.get('mobile_phone')
        # 如果手机号为空, 则不校验验证码
        if not user_obj:
            return sms_code
        conn = get_redis_connection()
        code = conn.get(user_obj.mobile_phone)
        if not code:
            raise ValidationError("验证码已超时, 请重新发送验证码")
        if code.decode('utf-8').strip() != sms_code:
            raise ValidationError("验证码错误, 请重新输入")
        # 删除验证码
        # conn.delete(user_obj.mobile_phone)
        # 返回验证码
        return sms_code


class LoginCodeSmsModelForm(BootStrapForm, forms.Form):
    username = forms.CharField(label='用户名或者邮箱或者手机号', max_length=32)
    password = forms.CharField(label='密码', max_length=64, widget=forms.PasswordInput(render_value=True))
    sms_code = forms.CharField(label='验证码', max_length=10)

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        pwd = self.cleaned_data['password']
        return md5(pwd)

    def clean_sms_code(self):
        sms_code = self.cleaned_data['sms_code']
        user_obj = self.cleaned_data.get('username')
        if not user_obj:
            return sms_code
        # 从session中获取验证码
        code = self.request.session.get('image_code')
        if not code:
            raise ValidationError("验证码已超时, 请重新发送")
        if code.strip().upper() != sms_code.upper():
            raise ValidationError("验证码错误, 请重新输入")
        return sms_code


